package com.andova.glcamera.device

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.ImageFormat
import android.graphics.SurfaceTexture
import android.hardware.Camera
import android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT
import android.os.Process
import com.andova.egl.OnTextureBridger
import com.andova.ext.d
import com.andova.ext.e
import com.andova.ext.i
import com.andova.glcamera.FRAME_ACTION_LAZY_PREVIEW
import com.andova.glcamera.FRAME_ACTION_PREVIEW
import com.andova.image.decodeToOriginalBitmap
import com.andova.libyuv.LibYUV
import kotlinx.coroutines.*
import java.io.File
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*

class Camera1Device(private val context: Context, private val angle: Int) : CameraCallback, Camera.PreviewCallback {
    private var ySize: Int = 0
    private var uvSize: Int = 0
    private var bufferY: ByteBuffer? = null
    private var bufferUV: ByteBuffer? = null

    companion object {
        private const val USE_BUFFER = false
    }

    private var frameData: ByteArray? = null
    private var cameraId = CAMERA_FACING_FRONT
    private var bufferUpdated = false
    private var builder: CameraBuilder? = null
    private var mCamera: Camera? = null
    private var mRender: OnTextureBridger? = null
    override fun getPreviewSize(): Size = Size(mCamera?.parameters?.previewSize?.width ?: 0, mCamera?.parameters?.previewSize?.height ?: 0)
    override fun getPictureSize(): Size = Size(mCamera?.parameters?.pictureSize?.width ?: 0, mCamera?.parameters?.pictureSize?.height ?: 0)
    override fun setFrameBufferUpdated(value: Boolean) = run { bufferUpdated = value }
    override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
        when (mRender?.obtainAction()) {
            FRAME_ACTION_PREVIEW, FRAME_ACTION_LAZY_PREVIEW -> return
        }
        frameData = data
        camera?.addCallbackBuffer(data)
        data ?: return
        if (USE_BUFFER) {
            bufferY?.position(0)
            bufferY?.put(data, 0, ySize)
            bufferUV?.position(0)
            bufferUV?.put(data, ySize, uvSize)
        }
        bufferUpdated = true
    }

    override fun takePicture(out: File?, callback: (dst: File?) -> Unit) {
        out ?: return callback(null)
        GlobalScope.launch {
            if (frameData == null) delay(50L)
            if (frameData == null) return@launch withContext(Dispatchers.Main) { callback(null) }
            mRender?.pause(false)
            val src = frameData!!.clone()
            val dst = ByteArray(src.size)
            val width = getPictureSize().width
            val height = getPictureSize().height
            LibYUV.RotateNV21(src, dst, angle, width, height)
            when (angle) {
                ANGLE_90, ANGLE_270 -> decodeToOriginalBitmap(dst, out, height, width)
                else -> decodeToOriginalBitmap(dst, out, width, height)
            }
            withContext(Dispatchers.Main) { callback(if (out.exists()) out else null) }
            frameData = null
        }
    }

    override fun startRecord(builder: RecorderBuilder) {
        TODO("Not yet implemented")
    }

    override fun stopRecord() {
        TODO("Not yet implemented")
    }

    override fun resetCaptureSession() {
        TODO("Not yet implemented")
    }

    override fun getSurfaceTexture(): SurfaceTexture? {
        TODO("Not yet implemented")
    }

    override fun openCamera(surfaceTexture: SurfaceTexture?, builder: CameraBuilder?) {
        this.builder = builder
        if (mCamera != null) return
        if (!checkCameraPermission(context)) {
            i("摄像头权限未打开，请打开后再试")
            return
        }
        // 只有一个摄相头，打开后置
        cameraId = if (Camera.getNumberOfCameras() == 1) CAMERA_ID_0 else builder?.cameraId ?: CAMERA_ID_0
        try {
            mCamera = Camera.open(cameraId)
            val mParameters = mCamera?.parameters
            mCamera?.parameters = mParameters
            if (CAMERA_FACING_FRONT == cameraId) i("前置摄像头已开启") else i("后置摄像头已开启")
        } catch (e: Exception) {
            e.printStackTrace()
            stopPreview()
            release()
            return
        }
        mCamera ?: return
        try {
            adjustCameraParameters()
            mCamera?.setDisplayOrientation(calcDisplayOrientation(angle))
            i("camera getPreviewSize width:${mCamera?.parameters?.previewSize?.width},height:${mCamera?.parameters?.previewSize?.height}")
            i("camera getPictureSize width:${mCamera?.parameters?.pictureSize?.width},height:${mCamera?.parameters?.pictureSize?.height}")
            startPreview(surfaceTexture, builder)
            builder?.stateCallback()?.onOpened(cameraId.toString())
        } catch (ex: Exception) {
            builder?.stateCallback()?.onError(cameraId.toString())
            stopPreview()
            release()
            e("Error starting camera preview: $ex")
        }
    }

    private fun startPreview(surfaceTexture: SurfaceTexture?, builder: CameraBuilder?) {
        if (builder?.usePreviewCallback == true) {
            mCamera?.parameters?.previewFormat = ImageFormat.NV21
            ySize = mCamera?.parameters?.previewSize?.run { width * height } ?: 0
            val bufSize = ySize * ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8
            uvSize = bufSize - ySize
            if (USE_BUFFER) {
                bufferY = ByteBuffer.allocateDirect(ySize).order(ByteOrder.nativeOrder())
                bufferUV = ByteBuffer.allocateDirect(uvSize).order(ByteOrder.nativeOrder())
            }
            mCamera?.addCallbackBuffer(ByteArray(bufSize))
            mCamera?.setPreviewCallbackWithBuffer(if (builder.usePreviewCallback) this else null)
        }
        mCamera?.setPreviewTexture(surfaceTexture)
        mCamera?.startPreview()
    }

    override fun stopPreview() {
        try {
            mCamera?.setPreviewCallback(null)
            mCamera?.stopPreview()
            mCamera?.release()
            mCamera = null
            builder?.stateCallback()?.onClosed(cameraId.toString())
        } catch (ex: Exception) {
            builder?.stateCallback()?.onError(cameraId.toString())
            e("release camera exception -> $ex")
        }
    }

    override fun release() {

    }

    override fun setBuilder(builder: CameraBuilder?) = run { this.builder = builder }
    override fun getBuilder(): CameraBuilder? = builder
    override fun isFrameBufferUpdated(): Boolean = bufferUpdated
    override fun getCameraId(): Int = builder?.cameraId ?: CAMERA_ID_0
    override fun getAspectRatio(): AspectRatio = builder?.aspectRatio ?: ASPECT_RATIO_4_3
    override fun isCameraOpened(): Boolean = mCamera != null
    override fun setBridger(bridger: OnTextureBridger?) {
        mRender = bridger
    }

    /**
     * 检查相机权限
     */
    private fun checkCameraPermission(context: Context?): Boolean {
        val status = context?.checkPermission(Manifest.permission.CAMERA, Process.myPid(), Process.myUid())
        return PackageManager.PERMISSION_GRANTED == status
    }

    private val mCameraInfo = Camera.CameraInfo()
    private val mPreviewSizes: SizeMap = SizeMap()
    private val mPictureSizes: SizeMap = SizeMap()
    private var mParameters: Camera.Parameters? = null
    private fun adjustCameraParameters() {
        mCamera ?: return
        mParameters = mCamera?.parameters
        mPreviewSizes.clear()
        for (size in mParameters?.supportedPreviewSizes ?: return) {
            mPreviewSizes.add(Size(size.width, size.height))
        }
        mPictureSizes.clear()
        for (size in mParameters?.supportedPictureSizes ?: return) {
            mPictureSizes.add(Size(size.width, size.height))
        }

        val ps: SortedSet<Size>? = mPreviewSizes.sizes(getAspectRatio())
        d("Camera preview size list -> $ps")
        val previewSize: Size? = builder?.previewSize(ps) ?: ps?.last()

        val ss: SortedSet<Size>? = mPictureSizes.sizes(getAspectRatio())
        d("Camera picture size list -> $ss")
        val pictureSize: Size? = builder?.pictureSize(ss) ?: ss?.last()

        mParameters?.apply {
            if (previewSize != null) setPreviewSize(previewSize.width, previewSize.height)
            if (pictureSize != null) setPictureSize(pictureSize.width, pictureSize.height)
            setRotation(calcCameraRotation(angle))
//            setAutoFocusInternal(mAutoFocus)
//            setFlashInternal(mFlash)
            mCamera?.parameters = this
        }
    }

    private fun isLandscape(orientationDegrees: Int): Boolean = orientationDegrees == 90 || orientationDegrees == 270
    private fun calcCameraRotation(screenOrientationDegrees: Int): Int {
        return if (mCameraInfo.facing == CAMERA_FACING_FRONT) {
            (mCameraInfo.orientation + screenOrientationDegrees) % 360
        } else { // back-facing
            val landscapeFlip = if (isLandscape(screenOrientationDegrees)) 180 else 0
            (mCameraInfo.orientation + screenOrientationDegrees + landscapeFlip) % 360
        }
    }

    private fun calcDisplayOrientation(screenOrientationDegrees: Int): Int {
        return if (mCameraInfo.facing == CAMERA_FACING_FRONT) {
            (360 - (mCameraInfo.orientation + screenOrientationDegrees) % 360) % 360
        } else { // back-facing
            (mCameraInfo.orientation - screenOrientationDegrees + 360) % 360
        }
    }
}